package com.googlecode.kipler.satisfiability.completion.strategy;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.googlecode.kipler.container.dl.Individual;
import com.googlecode.kipler.container.dl.World;
import com.googlecode.kipler.container.dl.WorldConstraintSystem;
import com.googlecode.kipler.satisfiability.completion.rule.CompletionRule;
import com.googlecode.kipler.satisfiability.completion.rule.EmptyDomainRule;
import com.googlecode.kipler.satisfiability.completion.rule.individual.ABoxRule;
import com.googlecode.kipler.satisfiability.completion.rule.individual.NegatedConceptDefinitionRule;
import com.googlecode.kipler.satisfiability.completion.rule.individual.NegatedRoleInclusionRule;
import com.googlecode.kipler.satisfiability.reasoner.Reasoner;
import com.googlecode.kipler.satisfiability.reasoner.ReasonerException;

public class PriorityBasedStrategy implements CompletionStrategy {
	private Reasoner reasoner;

	private World currentWorld;

	private WorldConstraintSystem currentWcs;

	private List<List<CompletionRule>> formulaRuleList = new ArrayList<List<CompletionRule>>();

	private List<IndividualRulePriorityListElement> indvRuleList = new ArrayList<IndividualRulePriorityListElement>();

	public PriorityBasedStrategy() {

	}

	public void apply(WorldConstraintSystem currentWcs)
			throws ReasonerException {
		this.currentWorld = currentWcs.worldIterator().next();
		this.currentWcs = currentWcs;
		// applyFormulaRules();
		applyIndvRules();
	}

	// private void applyFormulaRules() throws ReasonerException {
	// while (new RuleApplication(currentWcs, currentWorld)
	// .applySingleFormulaRule(formulaRuleList))
	// ;
	// }

	private void applyIndvRules() throws ReasonerException {
		CompletionRule compRule = new NegatedRoleInclusionRule(currentWcs,
				currentWorld);
		compRule.apply();
		compRule = new NegatedConceptDefinitionRule(currentWcs, currentWorld);
		compRule.apply();
		compRule = new EmptyDomainRule(currentWcs, currentWorld);
		compRule.apply();
		int ruleListIndex = 0;
		while (ruleListIndex < indvRuleList.size()) {
			List<ABoxRule> ruleList = indvRuleList.get(ruleListIndex)
					.getIndvRuleList();
			IndividualFeeder feeder = indvRuleList.get(ruleListIndex)
					.getFeeder();
			feeder.setWcs(currentWcs);
			feeder.setWorld(currentWorld);
			if (indvRuleList.get(ruleListIndex).isPreemptive()) {
				if (applyIndvRulesPreemptively(ruleList, feeder)) {
					ruleListIndex = 0;
				} else {
					ruleListIndex++;
				}
			} else {
				applyIndvRulesNonPreemptively(ruleList, feeder);
				ruleListIndex++;
			}
		}
	}

	private void applyIndvRulesNonPreemptively(List<ABoxRule> ruleList,
			IndividualFeeder feeder) throws ReasonerException {
		boolean ruleApplied;
		do {
			ruleApplied = false;
			Iterator<Individual> indvIter = feeder.iterator();
			while (indvIter.hasNext()) {
				Individual indv = indvIter.next();
				while (applySingleIndividualRule(ruleList, indv)) {
					ruleApplied = true;
				}
			}
		} while (ruleApplied);
	}

	private boolean applyIndvRulesPreemptively(List<ABoxRule> ruleList,
			IndividualFeeder feeder) throws ReasonerException {
		boolean ruleApplied = false;
		Iterator<Individual> indvIter = feeder.iterator();
		while (indvIter.hasNext() && !ruleApplied) {
			Individual indv = indvIter.next();
			ruleApplied = applySingleIndividualRule(ruleList, indv);
		}
		return ruleApplied;
	}

	private boolean applySingleIndividualRule(List<ABoxRule> ruleList,
			Individual indv) throws ReasonerException {
		boolean ruleApplied = false;
		Iterator<ABoxRule> ruleIter = ruleList.iterator();
		while (ruleIter.hasNext() && !ruleApplied) {
			ABoxRule rule = ruleIter.next();
			rule.setWcs(currentWcs);
			rule.setWorld(currentWorld);
			rule.setIndividual(indv);
			rule.apply();
			ruleApplied = rule.isApplied();
		}
		return ruleApplied;
	}

	@Override
	public Reasoner getReasoner() {
		return reasoner;
	}

	@Override
	public void setReasoner(Reasoner reasoner) {
		this.reasoner = reasoner;
	}

	public void addFormulaRules(List<CompletionRule> rule) {
		formulaRuleList.add(rule);
	}

	public void addIndividualRules(List<ABoxRule> rule, boolean preemptive,
			IndividualFeeder feeder) {
		indvRuleList.add(new IndividualRulePriorityListElement(rule,
				preemptive, feeder));
	}

}
